#!/bin/bash
# 执行此脚本能自动部署环境，自动启动两台Huawei CE12800交换机
# 通过telnet能连接到这两台交换机
# 并且交换机A的 interface GE 1/0/0 通过ovs直连 交换机B的interface GE 1/0/0
# 此外交换机A的 interface GE 1/0/1 通过ovs直连 交换机B的interface GE 1/0/1

source ../ovs_helper.sh

echo 正在关闭运行中的 CE12800 交换机...
ps -ef | grep -E "qemu-system.*CE128" | head -n -1 |awk -F ' ' '{print $2}' | xargs sudo kill

src_img="./base_ce12800.qcow2"


if [[ ! -r $src_img ]]; then
	echo 请把Huawei CE12800 的交换机镜像复制到本脚本所在目录下，并重命名为 $src_img 
	exit 1
fi

Aswitch_img="./Aswitch.qcow2"

Bswitch_img="./Bswitch.qcow2"

# if [[  -e xxx ]] 本质是 if test -e xxx
# 所以如果不清楚有哪些 -e 或其他指令 直接 man test 就能查看手册
if [[ ! -e $Aswitch_img ]]; then
	echo 正在为交换机克隆磁盘 $Aswitch_img
	qemu-img create -f qcow2 -b $src_img -F qcow2 $Aswitch_img
fi

if [[ ! -e $Bswitch_img ]]; then
	echo 正在为交换机克隆磁盘 $Bswitch_img
	qemu-img create -f qcow2 -b $src_img -F qcow2 $Bswitch_img
fi



args=(

	# 每个交换机都需要两张网卡来占位
		-device e1000,netdev=n1,mac=54:52:00:aa:aa:01
                -netdev tap,id=n1,ifname=switcha_1,script=no 	


		-device e1000,netdev=n2,mac=54:52:00:aa:aa:02
                -netdev tap,id=n2,ifname=switcha_2,script=no 

	
        	-drive file=$Aswitch_img,if=virtio,bus=0,unit=0,cache=none
		
		# 最原始的终端标准输入输出作为交换机命令行（Backspace退格键转义有问题，导致不能删除字符）
        	#-serial mon:stdio

		# 把 -serial mon:stdio 换成「TCP 套接字」即可让串口（以及 QEMU 自带的 monitor）同时变成可 telnet 的端口，
		# 虚拟机本身 -daemonize 以后就在后台跑，本地终端完全可关。
		# 串口和 monitor 都走同一个 TCP 端口
		#（telnet 进来后先看到串口输出，按 Ctrl-A 再按 c 就切到 monitor，再按 c 又切回串口）
		# 把串口 + monitor 一起绑定在 40001 端口，server 模式（监听），nowait 表示 QEMU 启动后不等客户端连接就继续跑。
		# mon:tcp::4321,server,nowait
		-serial mon:tcp::40001,server,nowait,telnet
		
		# telnet 默认行模式，会导致交换机tab的命令提示失效
		# 所以telnet登录成功以后要把telnet从默认的行模式切换到字符模式
		# telnet localhost 40002
		# Trying 127.0.0.1...
		# Connected to localhost.
		# Escape character is '^]'.

		# HUAWEI>
		# <HUAWEI>^]
		# telnet> mode char

)



b_args=(
		-device e1000,netdev=n1,mac=54:52:00:bb:bb:01
                -netdev tap,id=n1,ifname=switchb_1,script=no 	

		-device e1000,netdev=n2,mac=54:52:00:bb:bb:02
                -netdev tap,id=n2,ifname=switchb_2,script=no 	
        
		-drive file=$Bswitch_img,if=virtio,bus=0,unit=0,cache=none
	
		# mon:tcp::4321,server,nowait
		# 把串口 + monitor 一起绑定在 4321 端口，server 模式（监听），nowait 表示 QEMU 启动后不等客户端连接就继续跑。
		-serial mon:tcp::40002,server,nowait 
        	#-serial mon:stdio

	
)

for((i=0;i<2;i++));do
	# 经过在两台ce12800之间使用 interface GE 1/0/0 配置trunk 各自配置vlanif 10 ip 10.0.0.1/24 和 ip 10.0.0.2/24
	# 互相ping的时候
	# 使用 sudo ovs-tcpdump -i tap_ce_a_2 发现
	# GE 1/0/0 实际对应的是 第三个tap/br 

	# 所以从第三个接口（从1开始）才对应GE 1/0/0
	add_ovs_tap_pair br_ce$i tap_ce_a_$i tap_ce_b_$i   


	# 经过ovs抓包之后发现，我发现ovs bridge 还是把我的lacp协议包给过滤了，对端收不到lacp数据包
		# OVS 默认确实会丢弃 LACP 帧，原因跟 Linux bridge 一样：
		#它遵守 IEEE 802.1D 的 "Reserved destination MAC" 规则——
		#目的 MAC 落在 01:80:C2:00:00:00 ~ 0F 区间的帧 不允许被转发，而 LACP 用的 01:80:C2:00:00:02 正好命中这一条。

	# 根因定位
		#查看当前流表
		#sudo ovs-ofctl dump-flows ovs-br0
		#默认只有一条：
		#cookie=0x0, ... priority=0 actions=NORMAL
		# NORMAL 动作 = 传统 802.1D MAC 学习 + 802.1D 过滤，于是 LACP 被丢弃。

	# 解决方案
		#临时放行 01:80:C2-00-00-0x 全部保留地址
	sudo ovs-vsctl set bridge br_ce$i other-config:forward-bpdu=true
	sudo ip link set ovs-system up
	
	args+=(
		-device e1000,netdev=net$i,mac=54:52:00:aa:00:$(printf "%02d" $i)
                -netdev tap,id=net$i,ifname=tap_ce_a_$i,script=no #"${name}"

	)

	b_args+=(

		-device e1000,netdev=net$i,mac=54:52:00:bb:00:$(printf "%02d" $i)
                -netdev tap,id=net$i,ifname=tap_ce_b_$i,script=no 	

	)

done

create_shell_script(){
	echo 交换机$1的启动参数 

	# local -n 让 args_array_name 成为实参数组的引用，对 args_array_name 的读写就是直接改原数组。
	local -n args_array_name=$2
	

#	echo ${args_array_name[@]} | awk -v OFS=' \\\n' -v ORS=' ' '{
#    for(i=1;i<NF;i+=2)
#        printf "\t\t%s %s%s", $i,$(i+1), (i+2<NF? OFS : ORS)
#    }' >> $1.sh

	count=0


	printf "#!/bin/bash\n\nargs=( \n"  > $1.sh

	for n in ${args_array_name[@]}; do


		if [[ $n == -* ]]; then
			# bash 的 here-string (<<<) 不做任何额外转义，你给什么字节它就原样传给命令。
			# printf 的格式字符串里 \n 总是被解释成换行符；要想出现字面 \+n 必须再套一次转义或改用 %s 输出外部字符串。
			# printf " \\\\\n \t%s"  "$n" >> $1.sh  # 能用，但是太多转义理解起来有困难

			printf " \n\t%s"  "$n" >> $1.sh 
		else
			printf " %s" "$n" >> $1.sh
		fi
		
			
		((count++))
		
		if ((count == ${#args_array_name[@]})); then
			
			printf "\n )\n" >> $1.sh
			cat <<< 'qemu-system-x86_64 ${args[@]}' >> $1.sh 
		fi


	done
	
	tel_port=$(cat $1.sh  | grep -oE "-serial.*tcp::([0-9]+)" | awk -F "::" '{print $2}')

	echo echo 使用 telnet localhost $tel_port 来登录本交换机 >> $1.sh

	echo

	echo telnet 成功之后 按下 ctrl + ] 进入 telnet 控制台，然后再输入 mode char
	echo 这样就进入了 telnet 字符模式，才能使用 tab 命令自动补全

	chmod 777 $1.sh

#	echo  脚本内容

#	cat $1.sh

	echo


}

base_args=(
        -smp 2 -m 2048 -name CE12800-CE
        -uuid d3655400-3cb7-4815-a742-097e5fc9bed9
        -machine type=q35,accel=kvm
        -nodefaults
        -rtc base=utc
        -cpu host,vmx=off,svm=off
        -nographic
	-display none
	-daemonize
)

args+=(
	"${base_args[@]}"
)


b_args+=(
	"${base_args[@]}"
)



create_shell_script switch_a args

create_shell_script switch_b b_args

sudo ./switch_a.sh

sudo ./switch_b.sh
